package smallfrench.moteur.biblio.err;

import java.util.ArrayList;

import smallfrench.moteur.biblio.AClasse;
import smallfrench.moteur.biblio.AObjet;
import smallfrench.moteur.biblio.AnnotationMembre;
import smallfrench.moteur.biblio.RBooleen;
import smallfrench.moteur.biblio.RChaine;
import smallfrench.moteur.interp.ISortieConsole;
import smallfrench.moteur.interp.ProcessusScript;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;
import smallfrench.moteur.lexer.Position;

/**
 * Classe d’exception de base.<br>
 * 
 * 
 * @author antoine1023
 */
public class AException extends AObjet {

	public static final AClasse CLASSE = AClasse.creer(
			AException.class, "Exception");

	/**
	 * Equivalent de <code>assert</code>
	 * @param booleen Si faux, lance une exception.
	 */
	@AnnotationMembre
	public static void verifier(RBooleen booleen) {
		if (! booleen.valeur)
			new AException("Echec de vérification").lancer();
	}

	/**
	 * La pile des positions des appels de fonctions
	 */
	private final ArrayList<Position> pileAppels = new ArrayList<Position>();

	/**
	 * Une courte description de l’exception ou <code>null</code>
	 */
	protected String description;

	/**
	 * L’exception qui à causée cette exception ou <code>null</code>
	 */
	private AException cause;

	//------------------------------------------------------------------

	/**
	 * Crée une nouvelle exception à partir d’un tableau de paramètres :<br>
	 * Si le tableau contient une {@link AException}, ce sera la cause ;
	 * si il contient une {@link RChaine}, ce sera la description de la
	 * nouvelle exception.
	 * @param parametres
	 */
	@AnnotationMembre(parametresVariables = true)
	public AException(RObjet[] parametres) {
		this(parametres, "Exception");
	}

	/**
	 * 
	 * @param parametres
	 * @param descriptionParDefaut La description à utiliser si elle n’est pas
	 * précisée dans <code>parametres</code>
	 */
	protected AException(RObjet[] parametres,
			String descriptionParDefaut) {

		if (descriptionParDefaut == null)
			descriptionParDefaut = "Exception";
		description = descriptionParDefaut;

		for (RObjet p : parametres) {
			lireParam(p);
		}
	}

	/**
	 * 
	 * @param description Une courte description de l’exception
	 * @param cause L’exception qui à causée cette exception ou
	 * <code>null</code>.
	 */
	public AException(String description, AException cause) {
		if (description == null)
			description = "Exception";
		this.description = description;
		this.cause = cause;
	}

	/**
	 * 
	 * @param description Une courte description de l’exception
	 */
	public AException(String description) {
		this(description, null);
	}

	/**
	 * Construit une nouvelle exception sans description ni position
	 */
	public AException() {
		this((String) null, (AException) null);
	}

	//------------------------------------------------------------------

	/**
	 * Sert à lire les paramètres du constructeur
	 * @param o
	 */
	private void lireParam(RObjet o) {
		if (o instanceof RChaine) {
			description = ((RChaine) o).valeur;
		} else if (o instanceof AException && o != this) {
			cause = ((AException) o);
		}
	}

	public void ajouterPosition(Position p) {
		pileAppels.add(p);
	}

	public void empilerSiAucune(Position p) {
		if (pileAppels.isEmpty())
			pileAppels.add(p);
	}

	/**
	 * 
	 * @return La position en bas de la pile d’appels, c’est à dire la position
	 * où l’exception à été lancée.
	 */
	public Position getPosition() {
		if (pileAppels.isEmpty())
			return null;
		return pileAppels.get(0);
	}

	/**
	 * Lance (<i>throws</i>) cette exception.
	 * @throws RException
	 */
	@AnnotationMembre
	public void lancer() throws RException {
		throw new RException(this);
	}

	/**
	 * 
	 * @return Une nouvelle {@link RException} encapsulant cette exception
	 */
	public final RException getRException() {
		return new RException(this);
	}

	//------------------------------------------------------------------

	/**
	 * 
	 * @return Une {@link RChaine} décrivant les positions de la pile d’appels.
	 */
	@AnnotationMembre
	public RChaine obtenirChainePile() {
		return new RChaine(getChainePile());
	}


	/**
	 * 
	 * @return Une {@link String} décrivant l’exception, les positions de sa
	 * pile d’appels et ses causes.
	 */
	public String getChainePile() {
		String s = classe + " : " + toString() + "\n";
		for (Position p : pileAppels) {
			s += "à " + p + '\n';
		}

		if (cause != null)
			s += "Causée par :\n" + cause;

		return s;
	}


	/**
	 * Retourne seulement la description de l’exception.
	 */
	@Override
	@AnnotationMembre
	public RChaine enChaine() {
		String s = (description == null ? "Exception" : description) + "\n";
		return new RChaine(s);
	}


	public void afficher(ISortieConsole console) {
		console.ecrireLigne(getChainePile());
	}


	@AnnotationMembre
	public void afficher() {
		ProcessusScript ps = ProcessusScript.getInstance();
		afficher(ps.sortie);
	}
}
